/**
 *  irq.c - Interruption management.
 *
 *  Copyright (C) 2008-2009 ZhangHu
 *  All rights reserved.
 *  E-MAIL: anmnmnly@gmail.com
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "include/irq.h"


static word_t interrupt_cnt;    /* The number of interruption nesting */
static bool_t interrupt_flag;   /* Interruption flag */
static isr_head_t israrry[ISR_NUM]; /* An array store all interruption service handles */

/**
 * initirq - Initialize interruption management.
 * @return:
 *
 * @notes: Now, it is the only thing to initialize 'israrray[]'.
 */
void initirq(void) {
    uword_t i;

    interrupt_cnt = 0;
    interrupt_flag = FALSE;

    for(i = 0; i < ISR_NUM; i++) {
        israrry[i].link.next = &israrry[i].link;
        israrry[i].link.prev = &israrry[i].link;
        israrry[i].nr = 0;
    }
}



/**
 * int_nesting - return interruption nesting.
 * @return:
 *
 * @notes:
 */
uword_t int_nesting(void) {
    return(interrupt_cnt);
}


/**
 * int_flag - return interruption flag.
 * @return:
 *
 * @notes:
 */
bool_t int_flag(void) {
    return(interrupt_flag);
}



/**
 * int_nesting_sub -
 * @return:
 *
 * @notes:
 */
void int_nesting_sub(void) {
    interrupt_cnt--;

    if(interrupt_cnt < 0) {
        interrupt_cnt = 0;
    }
}



/**
 * int_flag_clr -
 * @return:
 *
 * @notes:
 */
void int_flag_clr(void) {
    interrupt_flag = FALSE;
}



/**
 * int_flag_set -
 * @return:
 *
 * @notes:
 */
void int_flag_set(void) {
    interrupt_flag = TRUE;
}



/**
 * isr_install - Installing a ISR for a interruption.
 * @IntNum: Serial number of interruption that vary from architecture.
 * @IrqNo: ISR Serial number for a interruption.
 * @phdl: Pointer to the entry of ISR.
 * @return:
 *
 * @notes: There are many ISRs for a given interruption.
 */
void isr_install(uword_t IntNum, uword_t IrqNo, void(*phdl)(void)) {
    isr_t *ptmp = NULL;
    list_t *list;

    if((phdl == NULL) || (IntNum >= ISR_NUM)) {
        return;
    }

    if(israrry[IntNum].nr > 0) {
        list = israrry[IntNum].link.next;

        do {
            ptmp = mac_find_entry(list, isr_t, link);
            if(ptmp->irq_no == IrqNo) {
                return;
            } else {
                list = list->next;
            }
        } while(list != israrry[IntNum].link.next);
    }

    ptmp = (isr_t*)allocate(sizeof(isr_t));
    if(ptmp != NULL) {
        ptmp->pisr = phdl;
        ptmp->irq_no = IrqNo;
        add_node_list_rear(&israrry[IntNum].link, &ptmp->link);
        israrry[IntNum].nr++;
    }
}



/**
 * isr_uninstall - Uninstalling a ISR of a interruption.
 * @IntNum: Serial number of interruption that vary from architecture.
 * @IrqNo: ISR Serial number for a interruption.
 * @return:
 *
 * @notes:
 */
void isr_uninstall(uword_t IntNum, uword_t IrqNo) {
    list_t *plist = NULL;
    isr_t *pisr = NULL;

    if((IntNum >= ISR_NUM) || (israrry[IntNum].nr == 0)) {
        return;
    }

    plist = israrry[IntNum].link.next;
    do {
        pisr = mac_find_entry(plist, isr_t, link);
        if(pisr->irq_no == IrqNo) {
            del_node_list(&israrry[IntNum].link, plist);
            reclaim(pisr);
            israrry[IntNum].nr--;
            break;
        }
        plist = plist->next;
    } while(plist != israrry[IntNum].link.next);
}



/**
 * isrHandler - Handle all interruptions.
 * @return:
 *
 * @notes: All interruption service routines have been installed
 *         in a array named 'israrry[]'. whenever any interruption occured
 *         this routine execute immediately.
 */
void isrHandler(void) {
    uword_t isrnum;
    list_t *plist = NULL;
    isr_t *phdl = NULL;

    interrupt_cnt++;
    int_flag_set();

    isrnum = get_isr_num();
    plist = israrry[isrnum].link.prev;
    do {
        if(plist !=  &israrry[isrnum].link) {
            phdl = mac_find_entry(plist, isr_t, link);
            phdl->pisr();
            plist = plist->prev;
        }
    } while(plist != israrry[isrnum].link.prev);
    isr_after_handle(isrnum);
    scheduler();
}

